package easier.app.x.calendar;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.Adapter;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import androidx.viewpager2.widget.ViewPager2;
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback;
import java.util.Calendar;
import java.util.Date;

public class CalendarView extends ViewGroup {

    /**
     * 日界面高度
     */
    private int mCellHeight;
    /**
     * 可滑动的月页面
     */
    private ViewPager2 mPager;
    /**
     * 月页面中构建日界面
     */
    private CalendarAdapter mCalendarAdapter;
    /**
     * 数据来源
     */
    private DateSource mDateSource;

    private ExpandFoldPage mExpandFoldPage;

    public CalendarView( Context context ) {

        super( context );
        createPager( context );
    }

    public CalendarView( Context context, AttributeSet attrs ) {

        super( context, attrs );
        createPager( context );
    }

    public CalendarView( Context context, AttributeSet attrs, int defStyleAttr ) {

        super( context, attrs, defStyleAttr );
        createPager( context );
    }

    public CalendarView( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes ) {

        super( context, attrs, defStyleAttr, defStyleRes );
        createPager( context );
    }

    /**
     * @param context 创建月滑动界面
     */
    private void createPager( Context context ) {

        mPager = new ViewPager2( context );

        removeAllViews();
        addView( mPager, new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT ) );

        mDateSource = new DateSource( new Date(), Integer.MAX_VALUE >> 1 );

        mExpandFoldPage = new ExpandFoldPage();

        mPager.registerOnPageChangeCallback( new OnPageChangeCallback() {

            @Override
            public void onPageScrollStateChanged( int state ) {

                mExpandFoldPage.mScrollState = state;
            }
        } );
    }

    @Override
    protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec ) {

        int widthSize = MeasureSpec.getSize( widthMeasureSpec );
        int heightSize = MeasureSpec.getSize( heightMeasureSpec );

        /* 至少需要6行才能显示完全 */
        mCellHeight = heightSize / 6;
        heightSize = mCellHeight * 6;

        /* 测量 */
        mPager.measure(
            MeasureSpec.makeMeasureSpec( widthSize, MeasureSpec.EXACTLY ),
            MeasureSpec.makeMeasureSpec( heightSize, MeasureSpec.EXACTLY )
        );

        /* 占用所有界面 */
        setMeasuredDimension( widthSize, heightSize );
    }

    @Override
    protected void onLayout( boolean changed, int l, int t, int r, int b ) {

        mPager.layout( l, t, r, b );
    }

    @Override
    public boolean dispatchTouchEvent( MotionEvent ev ) {

        return mExpandFoldPage.handleMotionEvent( ev );
    }

    /**
     * @param calendarAdapter 设置日界面适配器
     */
    public void setCalendarAdapter( CalendarAdapter calendarAdapter ) {

        mCalendarAdapter = calendarAdapter;
        mPager.setAdapter( new MonthAdapter() );
        /* 设置当前界面为数据中部,可以前后无限滑动 */
        mPager.setCurrentItem( Integer.MAX_VALUE >> 1, false );
        mDateSource.setBasePosition( Integer.MAX_VALUE >> 1 );
    }

    public CalendarAdapter getCalendarAdapter() {

        return mCalendarAdapter;
    }

    public void setDate( Date date ) {

        mDateSource.setBaseDate( date );
        mDateSource.setBasePosition( mPager.getCurrentItem() );
    }

    private int getCellHeight() {

        return mCellHeight;
    }

    /**
     * 月滑动页面使用的ViewHolder
     */
    private class MonthViewHolder extends ViewHolder {

        public MonthViewHolder( @NonNull View itemView ) {

            super( itemView );

            if ( getCalendarAdapter() == null ) {
                return;
            }

            RecyclerView recyclerView = (RecyclerView) itemView;
            recyclerView.setLayoutManager( new GridLayoutManager( recyclerView.getContext(), 7, RecyclerView.VERTICAL, false ) );
            recyclerView.setAdapter( new DayAdapter() );
        }

        private void bind( int position ) {

            Adapter< ? > adapter = ((RecyclerView) itemView).getAdapter();
            if ( adapter instanceof DayAdapter ) {
                Date date = mDateSource.getDate( position );
                ((DayAdapter) adapter).setDate( date );
            }
        }
    }

    /**
     * 月滑动页面使用的adapter
     */
    private class MonthAdapter extends Adapter< MonthViewHolder > {

        @NonNull
        @Override
        public MonthViewHolder onCreateViewHolder( @NonNull ViewGroup parent, int viewType ) {

            /* 使用RecyclerView构建月界面用于多样性构建界面 */
            RecyclerView recyclerView = new RecyclerView( parent.getContext() );
            recyclerView.setLayoutParams( new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT ) );
            return new MonthViewHolder( recyclerView );
        }

        @Override
        public void onBindViewHolder( @NonNull MonthViewHolder holder, int position ) {

            holder.bind( position );
        }

        @Override
        public int getItemCount() {

            /* 无限数量的日期 */
            return Integer.MAX_VALUE;
        }
    }

    /**
     * 月界面使用ViewHolder
     */
    private static class DayViewHolder extends ViewHolder {

        public DayViewHolder( @NonNull View itemView ) {

            super( itemView );
        }
    }

    /**
     * 月界面使用的Adapter
     */
    private class DayAdapter extends Adapter< DayViewHolder > {

        private final Date[] mDate = new Date[getItemCount()];

        @NonNull
        @Override
        public DayViewHolder onCreateViewHolder( @NonNull ViewGroup parent, int viewType ) {

            /* 委托创建 */
            View view = getCalendarAdapter().onCreateView( parent, viewType );
            return new DayViewHolder( view );
        }

        @Override
        public void onBindViewHolder( @NonNull DayViewHolder holder, int position ) {

            /* 设置日布局高度 */
            holder.itemView.getLayoutParams().height = getCellHeight();
            getCalendarAdapter().onBindView( holder.itemView, mDate[position], position );
        }

        @Override
        public int getItemCount() {

            return 7 * 6;
        }

        @Override
        public int getItemViewType( int position ) {

            Date date = mDate[position];
            return getCalendarAdapter().getViewType( position, date );
        }

        private void setDate( Date date ) {

            int dayOfWeek = CalendarUtils.getDayOfWeekForMonthFirstDay( date );
            int firstDayOfWeek = mDateSource.getFirstDayOfWeek();
            int offset;
            if ( dayOfWeek >= firstDayOfWeek ) {
                offset = dayOfWeek - firstDayOfWeek;
            } else {
                offset = 7 - firstDayOfWeek + dayOfWeek;
            }

            int dayOfMonth = CalendarUtils.getDayOfMonth( date ) - 1 /* -1从数量变为位置 */ + offset;
            for ( int i = 0; i < 7 * 6; i++ ) {
                Date day = CalendarUtils.getDateByAddDay( date, i - dayOfMonth );
                mDate[i] = day;
            }

            notifyItemRangeChanged( 0, 42 );
        }
    }

    /**
     * 用于根据页面之间的位置信息/显示模式计算日期
     */
    private static class DateSource {

        /**
         * 基准日期
         */
        private Date mBaseDate;
        private int mBasePosition;
        /**
         * 是否是月模式
         */
        private boolean isMonthMode = true;

        private int mFirstDayOfWeek = Calendar.MONDAY;

        private DateSource( Date baseDate, int basePosition ) {

            mBaseDate = baseDate;
            mBasePosition = basePosition;
        }

        private Date getBaseDate() {

            return mBaseDate;
        }

        private int getBasePosition() {

            return mBasePosition;
        }

        private void setBaseDate( Date baseDate ) {

            mBaseDate = baseDate;
        }

        private void setBasePosition( int basePosition ) {

            mBasePosition = basePosition;
        }

        private void setMonthMode( boolean monthMode ) {

            isMonthMode = monthMode;
        }

        private boolean isMonthMode() {

            return isMonthMode;
        }

        public void setFirstDayOfWeek( int firstDayOfWeek ) {

            mFirstDayOfWeek = firstDayOfWeek;
        }

        public int getFirstDayOfWeek() {

            return mFirstDayOfWeek;
        }

        private void reset( Date date, int position ) {

            mBaseDate = date;
            mBasePosition = position;
        }

        private void reset( Date date, int position, boolean isMonthMode ) {

            mBaseDate = date;
            mBasePosition = position;
            this.isMonthMode = isMonthMode;
        }

        private Date getDate( int position ) {

            if ( isMonthMode ) {
                return getMonthDate( position );
            } else {
                return getWeekDate( position );
            }
        }

        /**
         * 获取该位置的日期,每次增加或者减少一个月
         */
        private Date getMonthDate( int position ) {

            int step = position - mBasePosition;
            return CalendarUtils.getDateByAddMonth( mBaseDate, step );
        }

        /**
         * 获取该位置的日期,每次增加或者减少一周
         */
        private Date getWeekDate( int position ) {

            int step = position - mBasePosition;
            return CalendarUtils.getDateByAddWeek( mBaseDate, step );
        }
    }

    /**
     * 适配日布局
     */
    public interface CalendarAdapter {

        default int getViewType( int position, Date date ) {

            return 0;
        }

        View onCreateView( @NonNull ViewGroup parent, int viewType );

        void onBindView( @NonNull View view, Date current, int position );
    }


    /**
     * 用于竖直滑动时展开折叠当前页面
     */
    private class ExpandFoldPage {

        /**
         * 按下时位置信息
         */
        private float mDownX;
        private float mDownY;
        private float mLastX;
        private float mLastY;

        private boolean isHorizontalMove;
        private boolean isVerticalMove;
        private int mScrollState;

        /**
         * 拦截垂直滑动事件,并且通知给当前页面,竖直滑动距离
         *
         * @param ev 滑动事件
         *
         * @return 是否已经消费事件, true:已经消费
         */
        private boolean handleMotionEvent( MotionEvent ev ) {

            /* 只能在没有操作时上下滑动 */
            if ( mScrollState != ViewPager2.SCROLL_STATE_IDLE ) {
                return superDispatchTouchEvent( ev );
            }

            float x;
            float y;
            int currentItem = mPager.getCurrentItem();

            switch ( ev.getAction() ) {
                case MotionEvent.ACTION_DOWN:
                    x = ev.getRawX();
                    y = ev.getRawY();
                    mLastX = mDownX = x;
                    mLastY = mDownY = y;

                    return superDispatchTouchEvent( ev );

                case MotionEvent.ACTION_MOVE:

                    x = ev.getRawX();
                    y = ev.getRawY();

                    float dx = x - mLastX;
                    float dy = y - mLastY;
                    mLastX = x;
                    mLastY = y;

                    if ( !isHorizontalMove && !isVerticalMove ) {
                        float absX = Math.abs( dx );
                        float absY = Math.abs( dy );

                        /* 此时刚触发滑动事件 */
                        if ( absY > absX ) {
                            isVerticalMove = true;
                            isHorizontalMove = false;
                        }
                    }

                    if ( isVerticalMove ) {

                        return true;
                    }

                    return superDispatchTouchEvent( ev );

                default:
                    //x = ev.getRawX();
                    y = ev.getRawY();

                    if ( isVerticalMove ) {
                        isHorizontalMove = isVerticalMove = false;
                        return true;
                    }

                    isHorizontalMove = isVerticalMove = false;
                    return superDispatchTouchEvent( ev );
            }
        }

        private boolean superDispatchTouchEvent( MotionEvent ev ) {

            return CalendarView.super.dispatchTouchEvent( ev );
        }
    }
}
